# 定义内核和引导程序的目录别名
K=kernel
L=lib
U=user

# 设置RISC-V工具链前缀
TOOLPREFIX=riscv64-unknown-elf-
# 定义编译工具链的各个组件
CC=$(TOOLPREFIX)gcc      # C编译器
AS=$(TOOLPREFIX)as       # 汇编器
LD=$(TOOLPREFIX)ld       # 链接器
OBJCOPY=$(TOOLPREFIX)objcopy  # 目标文件格式转换工具
OBJDUMP = $(TOOLPREFIX)objdump  # 反汇编工具

# 编译选项设置
CFLAGS = -Wall -O -g -mcmodel=medany -nostdlib -nostdinc
CFLAGS += -ffreestanding
CFLAGS += -I include
# Wall: 显示所有警告信息
# O: 优化级别，这里是-O，即最高优化级别
# ggdb: 生成调试信息，用于GDB调试
# gdwarf-2: 生成DWARF-2格式的调试信息
# mcmodel=medany: 设置代码模型为medany
# nostdlib: 不链接标准库
# nostdinc: 不包含标准头文件
# freestanding: 不依赖于主机环境，适用于裸机环境
# I include: 指定头文件搜索路径为include目录


# 定义需要编译的目标文件列表
OBJS = \
    $K/entry.o \
    $K/start.o \
	$K/main.o \
	$K/uart.o \
	$K/console.o \
	$K/printf.o \
	$K/kernelvec.o \
	$K/trampoline.o \
	$K/trap.o \
	$K/kalloc.o \
	$K/vm.o \
	$K/proc.o \
	$K/spinlock.o \
	$K/swtch.o \
	$K/syscall.o \
	$L/string.o \

LDFLAGS = -z max-page-size=4096  # 设置最大页面大小为4096字节

# 编译内核目标
$K/kernel: $(OBJS) $K/kernel.ld $U/initcode
	$(LD) $(LDFLAGS) -T $K/kernel.ld -o $K/kernel $(OBJS)
	$(OBJDUMP) -S $K/kernel > $K/kernel.asm  # 反汇编生成汇编文件
	$(OBJDUMP) -t $K/kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $K/kernel.sym  # 生成符号表文件

$U/initcode: $U/initcode.S
	$(CC) $(CFLAGS) -march=rv64g -nostdinc -c $U/initcode.S -o $U/initcode.o
	$(LD) $(LDFLAGS) -N -e start -Ttext 0 -o $U/initcode.out $U/initcode.o
	$(OBJCOPY) -S -O binary $U/initcode.out $U/initcode
	$(OBJDUMP) -S $U/initcode.o > $U/initcode.asm
%.o: %.S
	$(CC) $(CFLAGS) -g -c -o $@ $<
	
ifndef CPUS
CPUS := 2
endif

QEMUOPTS = -machine virt -bios none -kernel $K/kernel -m 128M -smp $(CPUS) -nographic

# QEMU模拟器运行目标
qemu: $K/kernel
	qemu-system-riscv64 $(QEMUOPTS)

qemu-gdb: kernel
	qemu-system-riscv64 $(QEMUOPTS) -S -s

# 清理编译产生的文件
clean:
	rm -f */*.o */*.d */*.asm */*.sym $K/kernel